package com.hemerasolutions.v2trainer.component.table;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;

import org.primefaces.model.DefaultTreeNode;
import org.primefaces.model.TreeNode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;

import com.hemerasolutions.v2trainer.component.BaseComponent;
import com.hemerasolutions.v2trainer.component.interfaces.list.StepListCompInterface;
import com.hemerasolutions.v2trainer.component.interfaces.table.StatisticTableCompInterface;
import com.hemerasolutions.v2trainer.model.step.Step;
import com.hemerasolutions.v2trainer.model.training.TrainingStep;
import com.hemerasolutions.v2trainer.model.training.statistic.StatisticUnit;
import com.hemerasolutions.v2trainer.model.training.statistic.TrainingStatistic;
import com.hemerasolutions.v2trainer.service.interfaces.TrainingServiceInterface;


@Controller
@Component("statisticTableComp")
@Scope("prototype")
public class StatisticTableComp extends BaseComponent implements StatisticTableCompInterface {

	private static final long serialVersionUID = 1L;

	private TrainingStep trainingStep;
	
	private TrainingStatistic rooStatistic;

    private TreeNode rootNode;
    
	private List<StatisticUnit> fullArbritaryUnits;

	private StepListCompInterface parentTrainingList;
	
    @Autowired
    private TrainingServiceInterface trainingService;

	@Override
	public void refresh() {
		if(!this.trainingStep.getTraining().isTrainingSystem()){

			this.fullArbritaryUnits = this.trainingService.getArbitraryUnits();
			this.createAvailableColumns(this.trainingStep);
			this.calcStatistics();

		}
	}

	@Override
	public void calcStatistics(){

		if(this.trainingStep.getTraining().isTrainingSystem()){
			return;
		}
		
		boolean founded = false;
		
		List<TrainingStatistic> tsTemps = new ArrayList<TrainingStatistic>();
		
		List<TrainingStatistic> statistics = this.recursiveStatistic(this.trainingStep.getRootStep());
		
		this.trainingStep.getRootStatistic().clear(); 

		for(TrainingStatistic tsAux:statistics){
			founded = false;
			for(TrainingStatistic tStatistic:this.trainingStep.getTrainingStatistics()){
				if(tStatistic.isSumCompatible(tsAux)){
					tStatistic.clear();
					tStatistic.sum(tsAux);
					founded = true;
					tsTemps.add(tStatistic);
					this.trainingStep.getRootStatistic().sum(tStatistic);
					break;	
				}
			}if(!founded){
				tsTemps.add(tsAux);
				this.trainingStep.getRootStatistic().sum(tsAux);
			}			
		}

		this.trainingStep.setTrainingStatistics(tsTemps);
		
	}
	
	private List<TrainingStatistic> recursiveStatistic(Step step) {

		List<TrainingStatistic> stepStatisticsAux = new ArrayList<TrainingStatistic>();

		
		if(step.getSteps() != null & !step.getSteps().isEmpty()){
			for(Step stepAux:step.getSteps()){
				for(TrainingStatistic stepStatistic:this.recursiveStatistic(stepAux)){
					stepStatisticsAux.add(stepStatistic);
				}
				if(stepAux.getRepetition() > 1){
					stepStatisticsAux = this.multiplyStatistics(stepAux.getRepetition(),stepStatisticsAux);
				}
			}
		}
		
		if(step.getType() != Step.ROOT_STEP){
			stepStatisticsAux.add(new TrainingStatistic(this.trainingStep,step.calcStatistic()));
		}

		stepStatisticsAux = this.condenseStatistics(stepStatisticsAux);
		
		return stepStatisticsAux;
	}


	private List<TrainingStatistic> condenseStatistics(	List<TrainingStatistic> stepStatistics) {
		List<TrainingStatistic> stepStatisticsAux = new ArrayList<TrainingStatistic>();
		boolean founded = false;
		
		for(TrainingStatistic stepStatistic:stepStatistics){
			founded = false;
			for(TrainingStatistic stepStatisticAux:stepStatisticsAux){
				if(stepStatisticAux.isSumCompatible(stepStatistic)){
					founded = true;
					stepStatisticAux.sum(stepStatistic);
				}
			}
			if(!founded){
				stepStatisticsAux.add(stepStatistic);
			}
		}
		return stepStatisticsAux;
	}


	private List<TrainingStatistic> multiplyStatistics(int multiplier,	List<TrainingStatistic> stepStatisticsAux) {
		for(TrainingStatistic stepStatistic:stepStatisticsAux){
			stepStatistic.multiply(multiplier);
		}

		return stepStatisticsAux;
	}


	private void createAvailableColumns( TrainingStep ts) {

    	
		TreeNode availableColumns = new DefaultTreeNode("Root", null);
        TreeNode abunits = new DefaultTreeNode(this.localizedText("ArbitraryUnits"), availableColumns);
        
        availableColumns.setExpanded(true);

        
        for(StatisticUnit su:this.fullArbritaryUnits){
   			if(!ts.getStatisticUnits().contains(su)){
   	        	new DefaultTreeNode("column", new ColumnModel(this.localizedText(su.getName()), su.getName()), abunits);
   			}
        }

        this.rootNode = availableColumns;

	}	
	
	
	@Override
	public void toStatisticPopUp(ActionEvent ev){

		TrainingStatistic ts = (TrainingStatistic)ev.getComponent().getAttributes().get("trainingStatistic");
		this.parentTrainingList.getStatisticHandler().setTrainingStatistic(ts);
		this.parentTrainingList.refresh();

	}

	
	@Override
	public void treeToTable() {
        Map<String,String> params = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap();
        String unit = params.get("unit");

        for(StatisticUnit su:this.fullArbritaryUnits){
        	if(su.getName().equals(unit)){
                this.trainingStep.addArbritaryUnit(su);

            	for(TreeNode nodeAux : this.rootNode.getChildren()) {
            		for(TreeNode node : nodeAux.getChildren()){
                		ColumnModel model = (ColumnModel) node.getData();
                        if(model.getProperty().equals(unit)) {
                        	nodeAux.getChildren().remove(node);
                            break;
                        }
            		}
            	}
                break;
        	}
        }
        
        this.rootNode.getChildren().get(0).setExpanded(true);
        
        this.calcStatistics();
    }
	
    
	@Override
	public void tableToTree() {
		Map<String,String> params = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap();
		String unit = params.get("unit");

		
        for(StatisticUnit su:this.fullArbritaryUnits){
        	if(su.getName().equals(unit)){
                this.trainingStep.removeArbritaryUnit(su);
            	new DefaultTreeNode("column", new ColumnModel(this.localizedText(su.getName()), su.getName()), this.rootNode.getChildren().get(0));
                break;
        	}
        }		

        this.rootNode.getChildren().get(0).setExpanded(true);

	}
 	
    


	@Override
	public TrainingStep getTrainingStep() {
		return trainingStep;
	}


	@Override
	public void setTrainingStep(TrainingStep trainingStep) {
		this.trainingStep = trainingStep;
	}


	@Override
	public TreeNode getRootNode() {
		return rootNode;
	}


	@Override
	public void setRootNode(TreeNode rootNode) {
		this.rootNode = rootNode;
	}


	@Override
	public List<StatisticUnit> getFullArbritaryUnits() {
		return fullArbritaryUnits;
	}


	@Override
	public void setFullArbritaryUnits(List<StatisticUnit> fullArbritaryUnits) {
		this.fullArbritaryUnits = fullArbritaryUnits;
	}




	public TrainingStatistic getRooStatistic() {
		return rooStatistic;
	}


	public void setRooStatistic(TrainingStatistic rooStatistic) {
		this.rooStatistic = rooStatistic;
	}




	static public class ColumnModel implements Serializable {
	  	  
		private static final long serialVersionUID = 1L;
		private String header;
        private String property;
  
        public ColumnModel(String header, String property) {
            this.header = header;
            this.property = property;
        }
  
        public String getHeader() {
            return header;
        }
  
        public String getProperty() {
            return property;
        }
	}

	@Override
	public StepListCompInterface getParentTrainingList() {
		return parentTrainingList;
	}


	@Override
	public void setParentTrainingList(StepListCompInterface parentTrainingList) {
		this.parentTrainingList = parentTrainingList;
	}	
	

}
